﻿using System;
using System.Net;
using System.Net.Sockets;
using Development.Tools;
using Development.Network.Msg;

namespace Development.Network.Tcp
{
    public class TcpClient : ISender
    {
        public event TcpEventHandle EventClose;         //关闭了
        public event TcpMsgHandle EventRecvMsg;       //收到消息了
        public event TcpEventHandle EventReconnect;    //重连事件


        private readonly object _flagLock = new object();
        private Socket _sokect;
        private SocketAsyncEventArgs _recvEventArgs;   //接受异步
        private SocketAsyncEventArgs _sendEventArgs;   //发送异步
        private bool _sendAsync;                      //标识是否有发送异步事件
        /// <summary>
        /// buffer
        /// </summary>
        private DynamicBuffer _recvSplitBuffer;
        private DynamicBuffer _sendBuffer;
        private byte[] _recvBuffer;     //接收缓存

        private System.Timers.Timer _heartTimer;
        private string _host;
        private int _port;
        private bool _isConnect = false;

        public bool IsConnected { get { lock (_flagLock) { return _isConnect; } } }


        public TcpClient(int recvBufferSize)
        {
            _recvEventArgs = new SocketAsyncEventArgs();
            _recvEventArgs.UserToken = this;
            _recvEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
            _sendEventArgs = new SocketAsyncEventArgs();
            _sendEventArgs.UserToken = this;
            _sendEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);

            _recvSplitBuffer = new DynamicBuffer(recvBufferSize);
            _sendBuffer = new DynamicBuffer(recvBufferSize);
            _recvBuffer = new byte[recvBufferSize];
            _recvEventArgs.SetBuffer(_recvBuffer, 0, _recvBuffer.Length);
            _isConnect = false;
            _sendAsync = false;
        }

        public void Connect(string url, bool isWait = false)
        {
            string[] temp = url.Split(':');
            Connect(temp[0], int.Parse(temp[1]), isWait);
        }
        /// <summary>
        /// 开始连接
        /// </summary>
        /// <param name="host"></param>
        /// <param name="port"></param>
        public void Connect(string host, int port, bool isWait = false)
        {
            _host = host;
            _port = port;
            _isConnect = false;
            if (_sokect == null)
            {
                _sokect = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            }
            SocketAsyncEventArgs connectEventArgs = new SocketAsyncEventArgs();
            connectEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
            connectEventArgs.RemoteEndPoint = new IPEndPoint(IPAddress.Parse(host), port);
            bool willRaiseEvent = _sokect.ConnectAsync(connectEventArgs);
            if (!willRaiseEvent)
            {
                OnConnect(connectEventArgs);
            }
            if (isWait)
            {
                ThreadUtils.WaitTrue(ref _isConnect, new TimeSpan(0, 0, 0, 20, 0));
            }
        }

        /// <summary>
        /// 重连
        /// </summary>
        public void Reconnect()
        {
            if (EventReconnect != null)
            {
                EventReconnect(this);
            }
            Close();
            Connect(_host, _port);
        }

        void StartHeartJump()
        {
            CloseHeartJump();
            _heartTimer = new System.Timers.Timer(5000);
            _heartTimer.Elapsed += m_timer_Elapsed;
            _heartTimer.Start();
        }
        void m_timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            Msg.MsgBase msg = new Msg.MsgBase();
            msg._MsgId = 10;
            Send(msg.Pack());
        }
        void CloseHeartJump()
        {
            if (_heartTimer != null)
            {
                _heartTimer.Stop();
            }
        }

        private void OnConnect(SocketAsyncEventArgs e)
        {
            if (e.SocketError != SocketError.Success)
            {
                _isConnect = false;
                Close();
                Log.WriteLog(ELogLevel.LOG_INFO, string.Format("connect fail!host:{0},port:{1},reconnect", _host, _port));
                Reconnect();
            }
            else
            {
                StartHeartJump();
                _isConnect = true;
                PostRecv();
                Log.WriteLog(ELogLevel.LOG_INFO, string.Format("connect success!host:{0},port:{1}", _host, _port));
            }
        }

        private void OnRecv(SocketAsyncEventArgs e)
        {
            lock (_flagLock)
            {
                if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
                {
                    _recvSplitBuffer.WriteBuffer(_recvBuffer, 0, e.BytesTransferred);
                    //开始解包
                    //前四个字节为包的长度，后面的内容是协议数据
                    while (_recvSplitBuffer.DataCount >= 4)
                    {
                        int packetLength = BitConverter.ToInt32(_recvSplitBuffer.Buffer, 0);
                        if ((packetLength <= 0) || (packetLength > 10 * 1024 * 1024) || (_recvSplitBuffer.DataCount > 10 * 1024 * 1024))
                        {
                            //异常包
                            throw new Exception("异常包");
                        }

                        //消息报长度不够，等待下一次处理
                        if (_recvSplitBuffer.DataCount < packetLength)
                        {
                            break;
                        }
                        else
                        {
                            //解析包
                            MsgPacket msg = MsgPacket.SplitPacket(this, _recvSplitBuffer.Buffer, 0, packetLength);
                            //处理完消息包，重置缓存
                            _recvSplitBuffer.Clear(packetLength);
                            if (EventRecvMsg != null)
                            {
                                try
                                {
                                    EventRecvMsg(msg);
                                }
                                catch (Exception ex)
                                {
                                    Log.WriteLog(ELogLevel.LOG_ERROR, ex);
                                }
                            }
                        }

                    }
                    //继续投递接收消息
                    PostRecv();
                }
                else
                {
                    throw new Exception("on recv error");
                }
            }
        }

        private void PostRecv()
        {
            bool willRaiseEvent = _sokect.ReceiveAsync(_recvEventArgs);
            if (!willRaiseEvent)
            {
                OnRecv(_recvEventArgs);
            }
        }

        private void OnSend(SocketAsyncEventArgs e)
        {
            lock (_flagLock)
            {
                if (e.BytesTransferred != 0 && e.SocketError == SocketError.Success)
                {
                    _sendBuffer.Clear(e.BytesTransferred);
                    if (_sendBuffer.DataCount > 0)
                    {
                        _sendEventArgs.SetBuffer(_sendBuffer.Buffer, 0, _sendBuffer.DataCount);
                        bool willRaiseEvent = _sokect.SendAsync(_sendEventArgs);
                        if (!willRaiseEvent)
                        {
                            OnSend(_sendEventArgs);
                        }
                    }
                    else
                    {
                        _sendAsync = false;
                    }
                }
                else
                {
                    throw new Exception("on send error");
                }
            }

        }
        public void Send(byte[] buffer)
        {
            lock (_flagLock)
            {

                if (_sokect == null)
                {
                    return;
                }
                try
                {
                    _sendBuffer.WriteBuffer(buffer);
                    PostSend();
                }
                catch(Exception)
                {
                }
            }

        }
        private void PostSend()
        {
            if (_sendAsync)
            {
                return;
            }
            if (_sendBuffer.DataCount > 0)
            {
                _sendAsync = true;
                _sendEventArgs.SetBuffer(_sendBuffer.Buffer, 0, _sendBuffer.DataCount);
                bool willRaiseEvent = this._sokect.SendAsync(_sendEventArgs);
                if (!willRaiseEvent)
                {
                    OnSend(_sendEventArgs);
                }
            }
        }

        private void IO_Completed(object sender, SocketAsyncEventArgs e)
        {
            try
            {
                if (e.LastOperation == SocketAsyncOperation.Receive)
                    OnRecv(e);
                else if (e.LastOperation == SocketAsyncOperation.Send)
                    OnSend(e);
                else if (e.LastOperation == SocketAsyncOperation.Connect)
                    OnConnect(e);
                else
                    throw new ArgumentException("The last operation completed on the socket was not a receive or send");
            }
            catch (Exception)
            {
                Close();
            }
            finally
            {
                //防止程序崩溃
            }
        }


        public void Close()
        {
           
            lock (_flagLock)
            {
                CloseHeartJump();
                if (EventClose != null)
                {
                    EventClose(this);
                }
                Log.WriteLog(ELogLevel.LOG_INFO, "dis connect");
                if (_sokect != null)
                {
                    try
                    {
                        _sokect.Shutdown(SocketShutdown.Both);
                        _sokect.Close();
                    }
                    catch (Exception) { };
                }
                _sendAsync = false;
                _isConnect = false;
                _recvSplitBuffer.Clear();
                _sendBuffer.Clear();
                _sokect = null;
            }
        }
    }
}
